
#ifndef IEnumerable_t_h
#define IEnumerable_t_h

// Riccsson includes.
#include <System/Collections/Generic/IEnumerator.h>
#include <System/NotImplementedException.h>
#include <System/Collections/IEnumerable.h>

namespace Riccsson
{
	namespace System
	{
		namespace Collections
		{
			namespace Generic
			{
				template<typename T> 
				class enumerable_iterator
				{
				private:
					IEnumerator<T>* p;
					bool m_finished;
				public:

					//------------------------------------------------------------------------------
					// Constructor.
					enumerable_iterator(IEnumerator<T>* x, bool isEndIterator = false)
						: p(x)
						, m_finished(isEndIterator)
					{
					}

					//------------------------------------------------------------------------------
					// Copy constructor.
					enumerable_iterator(const enumerable_iterator<T>& mit)
						: p(mit.p)
						, m_finished(mit.m_finished)
					{
					}

					//------------------------------------------------------------------------------
					enumerable_iterator<T>& operator++()
					{
						bool movedToNext( p->MoveNext() );

						// If it not moved to next. Then the index was at the end and the foreach loop is finished.
						if(!movedToNext)
						{
							m_finished = true;
						}

						return *this;
					}

					//------------------------------------------------------------------------------
					enumerable_iterator<T> operator++(int)
					{
						enumerable_iterator<T> tmp(*this);
						operator++();
						return tmp;
					}

					//------------------------------------------------------------------------------
					bool operator==(const enumerable_iterator<T>& rhs) const
					{
						return m_finished == rhs.m_finished;
					}

					//------------------------------------------------------------------------------
					bool operator!=(const enumerable_iterator<T>& rhs) const
					{
						bool temp = m_finished != rhs.m_finished;
						if(!temp)
						{
							delete p;
						}
						return temp;
					}

					//------------------------------------------------------------------------------
					T operator*() const
					{
						return p->Current.get();
					}
				};

				template<typename T> 
				class enumerable_const_iterator
				{
				private:
					IEnumerator<T>* p;
					bool m_finished;
				public:

					//------------------------------------------------------------------------------
					// Constructor.
					enumerable_const_iterator(IEnumerator<T>* x, bool isEndIterator = false)
						: p(x)
						, m_finished(isEndIterator)
					{
					}

					//------------------------------------------------------------------------------
					// Copy constructor.
					enumerable_const_iterator(const enumerable_const_iterator<T>& mit)
						: p(mit.p)
						, m_finished(mit.m_finished)
					{
					}

					//------------------------------------------------------------------------------
					enumerable_const_iterator<T>& operator++()
					{
						bool movedToNext( p->MoveNext() );

						// If it not moved to next. Then the index was at the end and the foreach loop is finished.
						if(!movedToNext)
						{
							m_finished = true;
						}

						return *this;
					}

					//------------------------------------------------------------------------------
					enumerable_const_iterator<T> operator++(int)
					{
						enumerable_iterator<T> tmp(*this);
						operator++();
						return tmp;
					}

					//------------------------------------------------------------------------------
					bool operator==(const enumerable_const_iterator<T>& rhs) const
					{
						bool temp = m_finished == rhs.m_finished;
						return temp;
					}

					//------------------------------------------------------------------------------
					bool operator!=(const enumerable_const_iterator<T>& rhs) const
					{
						bool temp = m_finished != rhs.m_finished;
						if(!temp)
						{
							delete rhs.p;
						}
						return temp;
					}

					//------------------------------------------------------------------------------
					T operator*() const
					{
						T reference( p->Current );
						return reference;
						//return p->Current;
					}
				};

				// Summary:
				//     Exposes the enumerator, which supports a simple iteration over a collection
				//     of a specified type.
				//
				// Type parameters:
				//   T:
				//     The type of objects to enumerate.This type parameter is covariant. That is,
				//     you can use either the type you specified or any type that is more derived.
				//     For more information about covariance and contravariance, see Covariance
				//     and Contravariance in Generics.
				template<typename T> interface IEnumerable //<out T> : IEnumerable // TODO: Fix this. But how?
				{
					// Summary:
					//     Returns an enumerator that iterates through the collection.
					//
					// Returns:
					//     A System.Collections.Generic.IEnumerator<T> that can be used to iterate through
					//     the collection.
					public: virtual IEnumerator<T>* GetEnumerator() = 0;
					public: virtual IEnumerator<T>* GetEnumeratorConst() const { throw NotImplementedException("Subclasses of IEnumerable must override GetEnumeratorConst, to be able to loop over constant variables."); }

				public:

					typedef /*typename*/ enumerable_iterator<T> iterator;
					typedef /*typename*/ enumerable_const_iterator<T> const_iterator;

					//------------------------------------------------------------------------------
					iterator begin()
					{
						iterator iterator( GetEnumerator() );

						return iterator;
					}
					const_iterator begin() const
					{
						const_iterator iterator( GetEnumeratorConst() );

						return iterator;
					}

					//------------------------------------------------------------------------------
					const iterator cbegin() const
					{
						IEnumerator<T>* enumerator( 0 );
						iterator iterator( enumerator, true );

						return iterator;
					}

					//------------------------------------------------------------------------------
					iterator end()
					{
						IEnumerator<T>* enumerator( 0 );
						iterator iterator( enumerator, true );

						return iterator;
					}

					//------------------------------------------------------------------------------
					const_iterator end() const
					{
						const_iterator iterator( GetEnumeratorConst(), true );

						return iterator;
					}

					//------------------------------------------------------------------------------
					const iterator cend() const
					{
						iterator iterator( GetEnumeratorConst(), true );

						return iterator;
					}
				};
			}
		}
	}
}

#endif // IENUMERABLE_H